Home:ALL Converter>Replacing conditionals with polymorphism in java

Replacing conditionals with polymorphism in java

Ask Time:2019-08-22T23:26:36         Author:flankers33

Json Formatter

I have recently started practicing and learning java on my own, I'm having trouble understanding how I can use polymorphism for a better coding practice. how can I use polymorphism refactoring to get rid of if and else conditions in the following code?

I have a parent class Fruit, that has child classes kiwi and apple and bunch of other fruits. for ex: apple is sweet and kiwi is sour.

I have a parent class Human, that has child classes boy and girl. boy likes sweet flavor and girl likes sour. I created an array of fruit objects, and used if statement to check who likes each fruit based on its flavor.

Fruit[] fruit = new Fruit[2];
fruit[0] = new Apple ();
fruit[1] = new Kiwi ();
Boy boy1 = new Boy ();
String boyTaste = boy1.taste;
for (int i = 0; i < fruit.length; i++){
    if (fruit[i].flavor.equals (boyTaste)){
        System.out.println ("Boy likes " + fruit[i].name + " because " + fruit[i].taste ());
    } else {
        System.out.println ("Girl likes " + fruit[i].name +  " because " + fruit[i].taste ());
    }
}

how can I improve this following code by replacing conditions with polymorphism?

Author:flankers33,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/57612696/replacing-conditionals-with-polymorphism-in-java
cameron1024 :

First things first, never use == to compare Strings. Always use .equals(), e.g. str.equals(\"hello\").\n\nIt's not clear what you could improve using specifically polymorphism/inheritance. An argument could be made that Boy boy1 = new Boy(); should be replaced with Human boy1 = new Boy();, but in this case, since it is a local variable that is only used once, and is instantiated in the method, it doesn't make a huge amount of difference.\n\nA thing you may want to do is create methods on Human called likesFruit() and getMessage() like follows:\n\npublic abstract class Human {\n\n ...\n\n public boolean likesFruit(Fruit f) {\n return f.getFlavor().equals(getTaste());\n }\n\n public abstract String getMessage();\n\n ...\n}\n\n\ngetMessage() would be defined in Boy as:\n\npublic String getMessage() {\n return \"Boy likes %s because ...\"; // truncated for brevity\n}\n\n\nand a similar implementation for Girl.\n\nThen in your code, you can call:\n\nHuman human = new Boy();\nFruit[] fruits = ...\n\nfor (Fruit fruit : fruits) {\n if (human.likesFruit(fruit)) System.out.println(String.format(human.getMessage(), fruit.getName(), fruit.getFlavor()));\n}\n\n\nA few things to note:\n\n\nI have replaced field references with getters/setters (e.g. fruit.flavor becomes fruit.getFlavor(). This is good practice as it hides the necessary state from the caller, and allows you to perform logic when the getter is called.\nHuman is abstract. It is impossible to have a Human who is not also a Boy or a Girl (or Man/Woman etc, politics aside), and so should be illegal to instantiate a plain Human.\nSimilarly, getMessage() is abstract, since the implementation changes based on the subclass\n",
2019-08-22T15:39:59
Ivan Tomić :

I'd change the code in the following manner:\n\n\nif possible use enum for flavors - will help you avoid a lot of coding mistakes.\nreplace the find method with a more modern approach, maybe using streams API (if Java version allows)\nsplit the code into functionally well rounded methods, and distribute them across classes \nin a meaningful manner\n\n\nTo that point here's how I'd write it:\n\nI'd create a fruit class like this, and add flavor enum as a subclass (as flavor is a property of fruit)\n\npublic class Fruit {\nprivate FLAVOR flavor;\n\npublic Fruit( FLAVOR flavor ) {\n this.flavor = flavor;\n}\n\npublic FLAVOR getFlavor() {\n return this.flavor;\n}\n\npublic static enum FLAVOR {\n SWEET, \n SOUR;\n}\n\n\n}\n\nThe human class would look something like this\n\npublic class Human {\nprivate FLAVOR flavor;\npublic Human( FLAVOR flavor ) {\n this.flavor = flavor;\n}\n\npublic List<Fruit> findLikedFruit( List<Fruit> availableFruits ) {\n List<Fruit> likes = availableFruits.stream().filter( candidate -> Human.this.flavor == candidate.getFlavor() ).collect( Collectors.toList() );\n return likes;\n}\n\n\n}\n\nit basically contains the liked flavor, and a method which consumes the list of available fruits and returns a list containing only those liked\n\nfinally both fruit and human would be extended like this (example only on Fruit, but the principle applies)\n\npublic static class Apple extends Fruit {\n public Apple() {\n super(FLAVOR.SOUR);\n }\n}\n\n\nthe main class would look something like this:\n\npublic static void main( String[] args ) {\n List<Fruit> fruits = Arrays.asList( new Fruit[] {new Apple(), new Pear(), new Strawberry()});\n List<Fruit> boyLikes = new Boy().findLikedFruit( fruits );\n //if you wanna output it, you could do like so\n boyLikes.forEach(fruit -> System.out.println( fruit.getFlavor().name() ) );\n}\n\n\nThis makes the code way more safe, much more expandable in the future, and complies to modern java coding practices.\n\nHope that helps\n\nCheers",
2019-08-22T16:07:07
geneSummons :

After you fix the comparisons, in order to introduce polymorphism, you need to move the \"conditions\" from your main() method into the class definitions. At some level though, you can't get away from using if/else. But the for-loop in your \"polymorphic\" main method might look something like this:\n\nHuman person = new Boy(FlavorPreferences.SWEET)\nfor (int i = 0; i < fruit.length; i++) { \n System.out.println(person.likes(fruit[i]);\n}\n\n\nAlso - Just because the boy doesn't like a given \"fruit\", doesn't necessarily mean the girl likes that same fruit. Maybe some boys and girls have the same tastes in fruit flavors.",
2019-08-22T15:50:33
Zag :

Where you are running into trouble is that you are trying to use polymorphism for something that is a simple property -- the taste of the fruit. Polymorphism is for different behavior, not just different values. I tried to come up with an example with fruit, but failed. \n\nConsider, instead, bank accounts. Assume there is an abstract concept of a bank account that defines the interface, including a method withdrawMoney(double amount). \n\ninterface Account { \n boolean withdrawMoney(double amount);\n}\n\nclass SimpleAccount implements Account {\n double cash; // Assume initialized in constructor, not shown.\n boolean withdrawMoney(double amount) {\n if (amount <= cash) {\n cash -= amount;\n return true;\n } else {\n return false;\n }\n }\n}\nclass OverdraftProtectedAccount implements Account {\n double cash;\n Account overdraftAccount; // Assume both initialized in constructor\n boolean withdrawMoney(double amount) {\n if (amount <= cash) {\n cash -= amount;\n return true;\n } else if (overdraftAccount.withdrawMoney(amount - cash)) {\n cash = 0;\n return true;\n } else {\n return false;\n }\n }\n}\n\n\nThis example has, of course, lots of problems with timing, and with just returning a boolean, but the point is to show the idea that polymorphism is about behavior, not data.",
2019-08-22T18:40:28
yy